﻿using System;
using System.IO;
using System.Net;
using System.Xml;

namespace TTprotons.Crm.ExternalView.Service.Crm
{
    /// <summary>
    /// Handles the retrieval of Live Id ticket.
    /// </summary>
    public class LiveIdTicketAcquirer
    {
        #region SoapMarkUp

        private const string applicationId = "10"; // An arbitrary value that will be defined in the next non-alpha release

        private const string soapEnvelopeTemplate = @"<s:Envelope
            xmlns:s = ""http://www.w3.org/2003/05/soap-envelope""
            xmlns:wsse = ""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""
            xmlns:saml = ""urn:oasis:names:tc:SAML:1.0:assertion""
            xmlns:wsp = ""http://schemas.xmlsoap.org/ws/2004/09/policy""
            xmlns:wsu = ""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd""
            xmlns:wsa = ""http://www.w3.org/2005/08/addressing""
            xmlns:wssc = ""http://schemas.xmlsoap.org/ws/2005/02/sc""
            xmlns:wst = ""http://schemas.xmlsoap.org/ws/2005/02/trust"">
            <s:Header>
                <wlid:ClientInfo xmlns:wlid = ""http://schemas.microsoft.com/wlid"">
                    <wlid:ApplicationID>" + applicationId + @"</wlid:ApplicationID>
                </wlid:ClientInfo>
                <wsa:Action s:mustUnderstand = ""1"">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action>
                <wsa:To s:mustUnderstand = ""1"">{4:url}</wsa:To>
                <wsse:Security>
                    <wsse:UsernameToken wsu:Id = ""user"">
                        <wsse:Username>{0:userName}</wsse:Username>
                        <wsse:Password>{1:passWord}</wsse:Password>
                    </wsse:UsernameToken>
                </wsse:Security>
            </s:Header>
            <s:Body>
            <!--
                <wst:RequestSecurityToken Id = ""RST0"">
                    <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
                    <wsp:AppliesTo>
                        <wsa:EndpointReference>
                            <wsa:Address>http://live-int.com</wsa:Address>
                        </wsa:EndpointReference>
                    </wsp:AppliesTo>
                    <wsp:PolicyReference URI = ""MBI""></wsp:PolicyReference>
                </wst:RequestSecurityToken>
            -->
                <wst:RequestSecurityToken Id = ""RST1"">
                    <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
                    <wsp:AppliesTo>
                        <wsa:EndpointReference>
                            <wsa:Address>{2:partner}</wsa:Address>
                        </wsa:EndpointReference>
                    </wsp:AppliesTo>
                    <wsp:PolicyReference URI = ""{3:policy}""></wsp:PolicyReference>
                </wst:RequestSecurityToken>
            </s:Body>
        </s:Envelope>";

        #endregion

        /// <summary>
        /// Gets a WLID ticket for a specified user
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="partner">sitename, i.e. crmapp.www.local-titan.com</param>
        /// <param name="policy">auth policy, i.e. MBI_SSL</param>
        /// <param name="environment">Wlid environment, i.e. INT</param>
        /// <param name="requestTimeoutSeconds">time in seconds the request waits for response</param>
        /// <returns></returns>
        public string GetWlidTicket(string userName, string password, string partner, string policy, string environment, int requestTimeoutSeconds)
        {
            string url;
            switch (environment)
            {
                case "INT":
                    url = @"https://dev.login.live-int.com/wstlogin.srf";
                    break;
                case "PPE":
                    url = @"https://dev.login.live-ppe.com/wstlogin.srf";
                    break;
                case "":
                case null:
                case "PROD":
                    url = @"https://dev.login.live.com/wstlogin.srf";
                    break;
                default:
                    throw new ArgumentException("environment is not valid", "environment");
            }

            var request = WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/soap+xml; charset=UTF-8";
            request.Timeout = requestTimeoutSeconds * 1000;

            string soapEnvelope = string.Format(soapEnvelopeTemplate, userName, password, partner, policy, url);
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(soapEnvelope);
            using (var str = request.GetRequestStream())
            {
                str.Write(bytes, 0, bytes.Length);
                str.Close();
            }

            string xml;
            using (var response = request.GetResponse())
            {
                using (var reader = new StreamReader(response.GetResponseStream()))
                    xml = reader.ReadToEnd();

                response.Close();
            }

            var document = new XmlDocument();
            document.LoadXml(xml);

            var nsManager = new XmlNamespaceManager(document.NameTable);
            nsManager.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");

            var nodes = document.SelectNodes(@"//wsse:BinarySecurityToken/text()", nsManager);
            if (nodes != null && nodes.Count > 0 && nodes[0] != null)
                return nodes[0].Value;

            // The wsse:BinarySecurityToken element is missing. Examine the xml for error information
            throw new Exception("Failed to get Wlid ticket from WS-trust endpoint");
        }

        /// <summary>
        /// Gets a WLID ticket for a specified user
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <param name="partner">sitename, i.e. crmapp.www.local-titan.com</param>
        /// <param name="policy">auth policy, i.e. MBI_SSL</param>
        /// <param name="environment">Wlid environment, i.e. INT</param>
        /// <returns></returns>
        public string GetWlidTicket(string userName, string password, string partner, string policy, string environment)
        {
            return GetWlidTicket(userName, password, partner, policy, environment, 10);
        }
    }
}